package org.atlantis.sbe.actor

import scala.actors._
import Actor._
import java.util.Date

class AuctionService(seller: Actor, minBid: Int, closing: Date) extends Actor {

    // 拍卖完毕后，该Service还会继续开启3秒中，来告诉后来的客户-拍卖已经结束了
    val timeToShutdown = 3000
    val bidIncrement = 10

    def act() {
        var maxBid = minBid - bidIncrement
        var maxBidder: Actor = null
        loop {
            receiveWithin((closing.getTime() - new Date().getTime())) {
                case Offer(bid, client) =>
                    if (bid >= maxBid + bidIncrement) {
                        if (maxBid >= minBid)
                            maxBidder ! BeatenOffer(bid)
                        maxBid = bid
                        maxBidder = client
                        client ! BestOffer
                    } else {
                        client ! BeatenOffer(maxBid)
                    }
                case Inquire(client) => client ! Status(maxBid, closing)
                case TIMEOUT =>
                    if (maxBid >= minBid) {
                        val reply = AuctionConcluded(seller, maxBidder)
                        maxBidder ! reply
                        seller ! reply
                    } else seller ! AuctionFailed
                    receiveWithin(timeToShutdown) {
                        case Offer(_, client) => client ! AuctionOver
                        case TIMEOUT => exit
                    }
            }
        }
    }
}

object auction {

    val random = new scala.util.Random
    val minBid = 100

    val closing = new Date(new Date().getTime() + 4000)

    val seller = actor {}
    val auction = new AuctionService(seller, minBid, closing)

    def client(i: Int, increment: Int, top: Int) = new Actor {

        val name = "Client-" + i
        var max: Int = _
        var current: Int = _
        def log(msg: String) = println(name + ": " + msg)

        def act() {
            log("Started!")
            auction ! Inquire(this)
            receive {
                case Status(maxBid, _) =>
                    log("status(" + maxBid + ")")
                    max = maxBid
            }
            loop {
                if (max >= top) log("too expensive for me")
                else if (current < max) {
                    current = max + increment
                    Thread.sleep(1 + random.nextInt(1000))
                    auction ! Offer(current, this)
                }

                reactWithin(3000) {
                    case BestOffer => log("bestOffer(" + current + ")")
                    case BeatenOffer(maxBid) =>
                        log("beatenOffer(" + maxBid + ")")
                        max = maxBid
                    case AuctionConcluded(seller, maxBidder) =>
                        log("auctionConcluded")
                        exit
                    case AuctionOver =>
                        log("auctionOver")
                        exit
                    case TIMEOUT => exit
                }
            }
        }
    }

    def main(args: Array[String]) {
        seller.start()
        auction.start()

        client(1, 20, 200).start()
        client(2, 10, 300).start()
    }
}